from pymem import Pymem
from pymem.exception import MemoryReadError, WinAPIError, ProcessNotFound
from os import path
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import sys
import struct

def readPointer(ModuleName, offsets):
    def get_module_addr(ModuleName):
        modules = list(process.list_modules())
        for module in modules:
            if module.name == ModuleName:
                return module.lpBaseOfDll
        return None
    base_module = get_module_addr(ModuleName)
    if base_module is not None:
        try:
            current_address = base_module
            for i, offset in enumerate(offsets):
                if i < len(offsets) - 1:
                    current_address = process.read_int(current_address + offset)                    
                else:
                    if current_address == 0:
                        #print('当前地址为零')
                        return None
                    current_address += offset
                if current_address == 0:
                 #print('当前地址为零')
                 return None
            return current_address
        except MemoryReadError as e:
            #print(f'无法读取内存: {e}')
            return None
        except WinAPIError as e:
            #print(f'Windows API错误: {e}')
            return None
    else:
        #print('找不到模块基址')
        return None

def ConnectProcess(ProcessName):
    try:
        global process
        process = Pymem(ProcessName)
        #print(f'已找到 {ProcessName}')
        return process
    except ProcessNotFound:
        messagebox.showerror('错误', '未找到进程')

def ReadGBKString(addr, max_bytes=128):
    str_bytes = []
    for i in range(max_bytes):
        byte = process.read_bytes(addr + i, 1)
        if byte == b'\x00':
            break
        str_bytes.append(byte)
    try:
        decoded_str = b''.join(str_bytes).decode('shift-JIS', errors='ignore')
    except UnicodeDecodeError:
        return None

    return decoded_str

def CampJudgment(dz):
    dataTab = []
    CampTab = {64: '己方', 72: '友方', 80: '敌方', 96: '民方'}
    Camp = process.read_uchar(dz + 0xC)
    print(Camp)
    if Camp in CampTab:
            camp_name = CampTab[Camp]
            ValueTab = {0x15A: ('名称', 1), 0x17A: ('舰种', 1), 0x19A: ('驾驶员', 1), 0x1C0: ('HP', 0), 0x1C8: ('EN', 0), 0x1D0: ('SP', 0)}
            for offset, (name, is_string) in ValueTab.items():
                if is_string:
                    data = ReadGBKString(dz + offset)
                else:
                    data = process.read_int(dz + offset)
                dataTab.append(data)
            highest = ['最高速度', '加速性能', '旋回性能', '燃耗', '射程', '攻击力', '装甲性能', '回避性能']
            highest1 = {0: 'S', 1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E'}
            highest2 = {0: '', 1: '+', 2: '-'}
            for i in range(0, 8):
                sa, sb = int.from_bytes(process.read_bytes(dz + 0x2B1 + 0x4 * i + 1,1), 'little'), int.from_bytes(process.read_bytes(dz - 1 + 0x2B1 + 0x4 * i,1), 'little')
                sa = highest1.get(sa, sa)
                sb = highest2.get(sb, sb)
                dataTab.append(sa + sb)
            dataTab.insert(0, camp_name)
            dataTab.append(dz)
    if dataTab:
       return dataTab
      
def GetAllPerson():
    number = 0
    AllPerson = []
    for i in range(0, 70):
        dz = readPointer(ModuleName, [0x23502C, 0xC + i * 4, 0x0])
        if not dz:
            number += 1
            if number > 3:
                break
        else:
            SinglePerson = CampJudgment(dz)
            if SinglePerson:
                AllPerson.append(SinglePerson)
    return AllPerson

def KillsUnit(Unit):
    try:
        if process:
            number = 0
            for i in range(0, 70):
                dz = readPointer(ModuleName, [0x23502C, 0xC + i * 4, 0x0])
                if not dz:
                    number += 1
                    if number > 3:
                        break
                else:
                    Camp = process.read_int(dz + 0xC)
                    if Camp == Unit:
                        process.write_int(dz + 0x1C0, 0)
    except NameError:
        messagebox.showerror('错误', '未连接进程')
        return

def UpdateData():
    global AllPerson
    try:
        if process:
            input_frames = []
            AllPerson = GetAllPerson()
            for item in tree.get_children():
                tree.delete(item)
            for row in AllPerson:
                tree.insert('', 'end', values=row)
                tree.bind('<ButtonRelease-1>', on机体选择)   
    except NameError:
        messagebox.showerror('错误', '未连接进程')
        return

def on机体选择(event):
    global input_frames, update_button
    try:
        item = tree.selection()[0]
    except IndexError:
        return
    row_data = tree.item(item, 'values')
    if hasattr(on机体选择, 'input_frames'):
        for frame, entry, idx in input_frames:
            entry.delete(0, tk.END)
            entry.insert(0, row_data[idx])
    else:
        input_frames = []
        data_names = ['HP', 'EN', 'SP', '最高速度', '加速性能', '旋回性能', '燃耗', '射程', '攻击力', '装甲性能', '回避性能']
        for i, data in enumerate(row_data):
            if i < 4:
                continue
            if i >= len(data_names) + 4:
                break
            frame = tk.Frame(root)
            frame.pack(fill=tk.X)
            label_text = data_names[i-4]
            label = tk.Label(frame, text=f"{label_text}:")
            label.pack(side=tk.LEFT)
            entry = tk.Entry(frame)
            entry.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=5)
            entry.insert(0, data)
            input_frames.append((frame, entry, i))
    if not hasattr(on机体选择, 'update_button'):
        update_button = tk.Button(root, text="确认修改", command=lambda: 修改数据(item, row_data))
        update_button.pack(fill=tk.X)
    else:
        update_button.config(command=lambda: 修改数据(item, row_data))

    on机体选择.input_frames = input_frames
    on机体选择.update_button = update_button

def 修改数据(item, row_data):
    global tree, input_frames
    selected_items = tree.selection()
    if not selected_items or item not in tree.get_children():
        return
    now_data = tree.item(selected_items, 'values')
    for frame, entry, idx in input_frames:
        new_data = entry.get()
        if new_data != row_data[idx]:
            if idx in {4, 5, 6}:
                if new_data == '':
                    new_data = 0
                set_Tab = [0x1C0, 0x1C8, 0x1D0]
                try:
                    value = int(new_data)
                except ValueError:
                    entry.delete(0, tk.END)
                    entry.insert(0,row_data[idx])
                    continue
                address = int(now_data[-1]) + set_Tab[idx - 4 ]
                process.write_int(address, value)
                process.write_int(address - 4, value)
            else:
                if new_data == '':
                    new_data = 'S+'
                new_data = new_data.upper().strip()
                if not (new_data.isalpha() and new_data.upper() in 'ABCDES' or len(new_data) == 2 and new_data[0].isalpha() and new_data[1] in '+-' and new_data[0] in 'ABCDES'):
                        entry.delete(0, tk.END)
                        entry.insert(0,row_data[idx])
                        continue
                highest1 = {'S': 0, 'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5}
                highest2 = { '+': 1, '-': 2}
                set1 = highest1.get(new_data[0], None)
                if len(new_data) == 2:
                    set2 = highest2.get(new_data[1], None)
                else:
                    set2 = 0
                if set1 is not None:
                    address = int(now_data[-1]) + 0x2B1 + (idx-7)*0x4
                    process.write_uchar(address + 1, set1)
                    process.write_uchar(address - 1, set2)
            tree.item(item, values=now_data[:idx] + (new_data,) + now_data[idx+1:])

def display_data(data):
    global AllPerson, tree, root
    root = tk.Tk()
    root.title('银河天使工具:GAEL')
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    window_width = 1024
    window_height = 600
    x = (screen_width - window_width) // 2
    y = (screen_height - window_height) // 2
    root.geometry(f'{window_width}x{window_height}+{x}+{y}')

    menu_bar = tk.Menu(root)
    root.config(menu=menu_bar)

    KillsMenu = tk.Menu(menu_bar, tearoff=0)
    KillsMenu.add_command(label='全歼敌军', command=lambda:KillsUnit(80))
    KillsMenu.add_command(label='坑死友军', command=lambda:KillsUnit(72))
    KillsMenu.add_command(label='自杀', command=lambda:KillsUnit(64))
    menu_bar.add_cascade(label='我要开挂', menu=KillsMenu)

    UpdateMenu = tk.Menu(menu_bar, tearoff=0)
    menu_bar.add_cascade(label='刷新', command=UpdateData)
    menu_bar.add_cascade(label='重载进程', command=lambda:ConnectProcess(ProcessName))
    
    columns = [['阵营'], ['名称', 50], ['舰种'], ['驾驶员', 50], ['HP'], ['EN'], ['SP'], ['最高速度'], ['加速性能'], ['旋回性能'], ['燃耗'], ['射程'], ['攻击力'], ['装甲性能'], ['回避性能']]
    tree = ttk.Treeview(root, columns=[col[0] for col in columns], show='headings')

    for col in columns:
        tree.heading(col[0], text=col[0])
        if len(col) > 1:
            tree.column(col[0], width=col[1], anchor='center')
        else:
            tree.column(col[0],width=20,anchor='center')
    tree.pack(expand=True, fill='both')
    root.mainloop()


ProcessName = 'GAEL.exe'
ModuleName = 'GAEL.exe'
AllPerson = []
display_data(AllPerson)